package sia.tp1.genotype;

import java.util.ArrayList;
import java.util.List;

import sia.tp1.operator.Operator;

public class Genotype implements Comparable<Genotype> {

	OperatorTree tree;
	static double explodeP;
	int maxHeight;
	double aptitude;
	double acumAptitude;
	
	public Genotype(int maxHeight) {
		this.maxHeight = maxHeight;
		OperatorTree.explodeP = explodeP;
		OperatorTree.maxHeight = maxHeight;
		tree = new OperatorTree(maxHeight, 0);
	}
	
	public void mutate(double mutationP) {
		if(Math.random() < mutationP) {
			tree = new OperatorTree(maxHeight, 0);
		} else {
			tree.mutateChilds(mutationP);
			tree.updateTree(0);
		}
		
		if( tree.height > maxHeight )
			return;
	}

	public List<Genotype> cross(Genotype genotype) {
		
		List<Genotype> crossedGenotypes = new ArrayList<Genotype>();
		Genotype crossedGen1 = this.clone();
		Genotype crossedGen2 = genotype.clone();
		
		int crossPoint1 = getCrossPoint(this, -1);
		int crossPoint2 = getCrossPoint(genotype, crossPoint1);
	
		OperatorTree subTree1 = crossedGen1.tree.GetNodeWrapper(crossPoint1);
		OperatorTree subTree2 = crossedGen2.tree.GetNodeWrapper(crossPoint2);
		
		//Switcheo las cosas
		switchTrees(subTree1,subTree2);
		
		//Actualizo los heihgts y levels los 2 arboles
		crossedGen1.updateTree();
		crossedGen2.updateTree();
		
		if( crossedGen1.tree.height > maxHeight )
			return null;
		
		if( crossedGen2.tree.height > maxHeight )
			return null;
		
		crossedGenotypes.add(crossedGen1);
		crossedGenotypes.add(crossedGen2);
		
		return crossedGenotypes;
	}


	private void switchTrees(OperatorTree subTree1, OperatorTree subTree2) {
		OperatorTree leftTreePivot = subTree1.leftTree;
		OperatorTree rightTreePivot = subTree1.rightTree;
		int heightPivot = subTree1.height;
		Operator operatorPivot = subTree1.operator;
	
		subTree1.leftTree = subTree2.leftTree;
		subTree1.rightTree = subTree2.rightTree;
		subTree1.height = subTree2.height;
		subTree1.operator = subTree2.operator;
		
		subTree2.leftTree = leftTreePivot;
		subTree2.rightTree = rightTreePivot;
		subTree2.height = heightPivot;
		subTree2.operator = operatorPivot;
	}

	private int getCrossPoint(Genotype genotype, int from) {
		int point;
		
		if( from == -1 )
			return (int) Math.floor( Math.random() * ( genotype.getNodesCount() ) );
		
		do {
			point = (int) Math.floor( Math.random() * ( genotype.getNodesCount() ) );
		}
		while( (tree.GetNodeWrapper(from).level + genotype.tree.GetNodeWrapper(point).height > maxHeight)
				|| tree.GetNodeWrapper(from).height + genotype.tree.GetNodeWrapper(point).level > maxHeight);
			
		return point;
	}

	@Override
	public Genotype clone() {
		Genotype clone = new Genotype(maxHeight);
		clone.tree = tree.clone();
		return clone;
	}

	public boolean eval(List<Boolean> input) throws Exception {
		return tree.eval(input);		
	}
	
	public int getNodesCount() {
		return this.tree.getNodesCount();
	}
	
	public double getAptitude() {
		return aptitude;
	}

	public void setAptitude(double aptitude) {
		this.aptitude = aptitude;
	}

	public double getAcumAptitude() {
		return acumAptitude;
	}

	public void setAcumAptitude(double acumAptitude) {
		this.acumAptitude = acumAptitude;
	}

	public static double getExplodeP() {
		return explodeP;
	}

	public static void setExplodeP(double explodeP2) {
		Genotype.explodeP = explodeP2;
	}
	
	public OperatorTree getTree() {
		return tree;
	}

	@Override
	public String toString() {
		return tree.toString();
	}

	@Override
	public int compareTo(Genotype o) {
		return (int)(o.aptitude - aptitude);
	}
	
	public void updateTree() {
		tree.updateTree(0);
	}
}
